1 /*
2 * Copyright (C) 2009 The Guava Authors
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 package com.google.common.util.concurrent;
18
19 import com.google.common.annotations.Beta;
20 import com.google.common.base.Supplier;
21 import com.google.common.base.Throwables;
22
23 import java.util.concurrent.Executor;
24 import java.util.concurrent.TimeUnit;
25 import java.util.concurrent.TimeoutException;
26 import java.util.logging.Level;
27 import java.util.logging.Logger;
28
29 /**
30 * Base class for services that can implement {@link #startUp}, {@link #run} and
31 * {@link #shutDown} methods. This class uses a single thread to execute the
32 * service; consider {@link AbstractService} if you would like to manage any
33 * threading manually.
34 *
35 * @author Jesse Wilson
36 * @since 1.0
37 */
38 @Beta
39 public abstract class AbstractExecutionThreadService implements Service {
40 private static final Logger logger = Logger.getLogger(
41 AbstractExecutionThreadService.class.getName());
42
43 /* use AbstractService for state management */
44 private final Service delegate = new AbstractService() {
45 @Override protected final void doStart() {
46 Executor executor = MoreExecutors.renamingDecorator(executor(), new Supplier<String>() {
47 @Override public String get() {
48 return serviceName();
49 }
50 });
51 executor.execute(new Runnable() {
52 @Override
53 public void run() {
54 try {
55 startUp();
56 notifyStarted();
57
58 if (isRunning()) {
59 try {
60 AbstractExecutionThreadService.this.run();
61 } catch (Throwable t) {
62 try {
63 shutDown();
64 } catch (Exception ignored) {
65 logger.log(Level.WARNING,
66 "Error while attempting to shut down the service"
67 + " after failure.", ignored);
68 }
69 throw t;
70 }
71 }
72
73 shutDown();
74 notifyStopped();
75 } catch (Throwable t) {
76 notifyFailed(t);
77 throw Throwables.propagate(t);
78 }
79 }
80 });
81 }
82
83 @Override protected void doStop() {
84 triggerShutdown();
85 }
86 };
87
88 /**
89 * Constructor for use by subclasses.
90 */
91 protected AbstractExecutionThreadService() {}
92
93 /**
94 * Start the service. This method is invoked on the execution thread.
95 *
96 * <p>By default this method does nothing.
97 */
98 protected void startUp() throws Exception {}
99
100 /**
101 * Run the service. This method is invoked on the execution thread.
102 * Implementations must respond to stop requests. You could poll for lifecycle
103 * changes in a work loop:
104 * <pre>
105 * public void run() {
106 * while ({@link #isRunning()}) {
107 * // perform a unit of work
108 * }
109 * }
110 * </pre>
111 * ...or you could respond to stop requests by implementing {@link
112 * #triggerShutdown()}, which should cause {@link #run()} to return.
113 */
114 protected abstract void run() throws Exception;
115
116 /**
117 * Stop the service. This method is invoked on the execution thread.
118 *
119 * <p>By default this method does nothing.
120 */
121 // TODO: consider supporting a TearDownTestCase-like API
122 protected void shutDown() throws Exception {}
123
124 /**
125 * Invoked to request the service to stop.
126 *
127 * <p>By default this method does nothing.
128 */
129 protected void triggerShutdown() {}
130
131 /**
132 * Returns the {@link Executor} that will be used to run this service.
133 * Subclasses may override this method to use a custom {@link Executor}, which
134 * may configure its worker thread with a specific name, thread group or
135 * priority. The returned executor's {@link Executor#execute(Runnable)
136 * execute()} method is called when this service is started, and should return
137 * promptly.
138 *
139 * <p>The default implementation returns a new {@link Executor} that sets the
140 * name of its threads to the string returned by {@link #serviceName}
141 */
142 protected Executor executor() {
143 return new Executor() {
144 @Override
145 public void execute(Runnable command) {
146 MoreExecutors.newThread(serviceName(), command).start();
147 }
148 };
149 }
150
151 @Override public String toString() {
152 return serviceName() + " [" + state() + "]";
153 }
154
155 @Override public final boolean isRunning() {
156 return delegate.isRunning();
157 }
158
159 @Override public final State state() {
160 return delegate.state();
161 }
162
163 /**
164 * @since 13.0
165 */
166 @Override public final void addListener(Listener listener, Executor executor) {
167 delegate.addListener(listener, executor);
168 }
169
170 /**
171 * @since 14.0
172 */
173 @Override public final Throwable failureCause() {
174 return delegate.failureCause();
175 }
176
177 /**
178 * @since 15.0
179 */
180 @Override public final Service startAsync() {
181 delegate.startAsync();
182 return this;
183 }
184
185 /**
186 * @since 15.0
187 */
188 @Override public final Service stopAsync() {
189 delegate.stopAsync();
190 return this;
191 }
192
193 /**
194 * @since 15.0
195 */
196 @Override public final void awaitRunning() {
197 delegate.awaitRunning();
198 }
199
200 /**
201 * @since 15.0
202 */
203 @Override public final void awaitRunning(long timeout, TimeUnit unit) throws TimeoutException {
204 delegate.awaitRunning(timeout, unit);
205 }
206
207 /**
208 * @since 15.0
209 */
210 @Override public final void awaitTerminated() {
211 delegate.awaitTerminated();
212 }
213
214 /**
215 * @since 15.0
216 */
217 @Override public final void awaitTerminated(long timeout, TimeUnit unit) throws TimeoutException {
218 delegate.awaitTerminated(timeout, unit);
219 }
220
221 /**
222 * Returns the name of this service. {@link AbstractExecutionThreadService}
223 * may include the name in debugging output.
224 *
225 * <p>Subclasses may override this method.
226 *
227 * @since 14.0 (present in 10.0 as getServiceName)
228 */
229 protected String serviceName() {
230 return getClass().getSimpleName();
231 }
232 }